from typing import Dict, Union, TypedDict, List, Optional
import json
import time
import random

from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from django.core.handlers.wsgi import WSGIRequest
from celery.result import AsyncResult

from . import forms
from scraper_bitrix import tasks
from core.celery import app as celery_app
from .scraper import utils as scraper_utils


class ScraperSettings(TypedDict):
    start_of_restart_interval: int
    end_of_restart_interval: int
    stop_words: Optional[str]


class ScraperBitrix:
    """ Инструменты относящиеся к приложению scraper_bitrix """
    pass


class ScraperBitrixHome(ScraperBitrix):
    """ Инструменты относящиеся к домашней странице парсинга """

    def get_home_page(self, request: WSGIRequest) -> HttpResponse:
        """ Получаем домашнюю страницу """

        scraper_settings = ScraperBitrixSettings().get_scraper_settings(
            request
        )
        form = forms.ScraperBitrixForm(initial=scraper_settings)
        context = {'form': form}
        return render(request, 'scraper_bitrix/scraper_bitrix_home.html', context)
    

    def get_necessary_information(self, request: WSGIRequest) -> HttpResponse:

        if not request.POST: # Отображение домашней страницы
            return self.get_home_page(request)


class ScraperBitrixSettings(ScraperBitrix):
    """ Инструменты относящиеся к настройке парсинга """

    def get_scraper_settings(
        self,
        request: WSGIRequest
    ) -> Union[Dict, ScraperSettings]:
        """ Получам выбранные пользователем настройке при предыдущем парсинге """

        scraper_settings = request.session.get(
            'scraper_settings',
            {}
        )
        if scraper_settings: return ScraperSettings(**scraper_settings)
        else: return {}


    def get_reboot_time(self, scraper_settings: ScraperSettings) -> int:
        """ Получаем время перезагрузки """

        result = random.randint(
            scraper_settings['start_of_restart_interval'],
            scraper_settings['end_of_restart_interval'],
        )
        return result


    def get_desired_type_of_scraper_settings(
        self,
        request: WSGIRequest
    ) -> ScraperSettings:
        """ Получаем настройки парсинга в нужном виде """

        list_of_settings = {}
        body = json.loads(request.body)
        parsing_settings = body['parserSettingElements']

        for element in parsing_settings:
            if element[0] == 'csrfmiddlewaretoken': continue

            try:
                element[1] = int(element[1])
            except ValueError:
                pass

            element = dict([element])
            list_of_settings.update(element)

        return ScraperSettings(**list_of_settings)


    def save_settings(
        self,
        request: WSGIRequest,
        scraper_settings: ScraperSettings
    ) -> JsonResponse:
        """ Сохраняем настройки парсинга """

        for element in scraper_settings:

            if not request.session.get('scraper_settings'):
                request.session['scraper_settings'] = {}

            request.session['scraper_settings'][element] = scraper_settings[element]

        request.session.modified = True
        return JsonResponse({'status': 200})


    def perform_necessary_action(self, request: WSGIRequest) -> JsonResponse:
        """ Выполняем необходимое действие """

        if 'save_settings' in request.GET:
            scraper_settings = self.get_desired_type_of_scraper_settings(request)
            return self.save_settings(request, scraper_settings)
        else:
            message = f'Такой аргумент не обрабатывается {request.GET}'
            raise ValueError(message)


class ScraperBitrixStartup(ScraperBitrix):

    def save_running_task(
        self,
        request: WSGIRequest,
        task: AsyncResult
    ) -> None:
        """ Сохраняем запущенную задачу """

        task_id = task.id

        if not request.session.get('running_tasks'):
            request.session['running_tasks'] = {}

        task_designation = int(time.time())
        request.session['running_tasks'][task_designation] = task_id
        request.session.modified = True


    def get_list_of_all_tasks(self, request: WSGIRequest) -> Dict[str, str]:
        """ Получаем все задачи """

        list_of_running_task = request.session.get('running_tasks', {})
        return list_of_running_task


    def delete_task_from_list_of_all_tasks(
        self,
        request: WSGIRequest,
        task_designation: Union[str, int]
    ) -> None:
        """ Удалаляем задачу из списка всех задач """

        task_designation = str(task_designation)
        del request.session['running_tasks'][task_designation]
        request.session.modified = True


    def clear_list_of_all_tasks(self, request: WSGIRequest) -> None:
        """ Очищаем список всех задач """

        request.session['running_tasks'] = {}
        request.session.modified = True


    def stop_task(self, task_id: str) -> None:
        """ Останавливаем задачу """
        celery_app.control.revoke(task_id, terminate=True)


    def stop_all_tasks(self, request: WSGIRequest) -> None:
        """ Останавливаем все задачи """

        all_tasks = self.get_list_of_all_tasks(request)

        for task_id in all_tasks.values():
            self.stop_task(task_id)


    def stop_and_clear_all_tasks(self, request: WSGIRequest) -> None:
        """ Останавливаем и очищаем все задачи """

        self.stop_all_tasks(request)
        self.clear_list_of_all_tasks(request)


    def get_initial_running_task(
        self,
        request: WSGIRequest
    ) -> List[str]:
        """ Получаем начальную запущенную задачу """

        list_of_running_task = self.get_list_of_all_tasks(request)
        task_designation = str(min(list(
            map(int, list_of_running_task.keys())
        )))
        task_id = list_of_running_task[task_designation]
        return [task_designation, task_id]


    def stop_scraper(self, request: WSGIRequest) -> JsonResponse:
        """ Останавливаем парсер """

        task_designation, task_id = self.get_initial_running_task(request)
        self.stop_task(task_id)
        self.delete_task_from_list_of_all_tasks(request, task_designation)
        scraper_utils.clear_scraper_logging()
        return JsonResponse({'status': 200})


    def run_scraper(self, request: WSGIRequest) -> JsonResponse:
        """ Запускаем парсер """

        self.stop_and_clear_all_tasks(request) # Удаляем все лишние задачи

        scraper_settings = ScraperBitrixSettings().get_desired_type_of_scraper_settings(
            request
        )
        reboot_time = ScraperBitrixSettings().get_reboot_time(scraper_settings)
        task = tasks.run_scraper.delay(
            reboot_time,
            scraper_settings['stop_words']
        )
        self.save_running_task(request, task)
        return JsonResponse({'status': 200})


    def perform_necessary_action(self, request: WSGIRequest) -> JsonResponse:
        """ Выполняем необходимое действие """

        if 'start_scraper' in request.GET:
            return self.run_scraper(request)
        elif 'stop_scraper' in request.GET:
            return self.stop_scraper(request)
        else:
            message = f'Такой аргумент не обрабатывается {request.GET}'
            raise ValueError(message)


    def get_latest_parsing_actions(self, request: WSGIRequest) -> JsonResponse:
        """ Получаем последние действия парсинга """

        latest_parsing_actions = {}

        if request.GET.get('latest_action') == 'full':
            last_parsing_action = scraper_utils.get_all_parsing_actions()
        else:
            last_parsing_action = scraper_utils.get_latest_parsing_action(
                request.GET.get('latest_action_id'),
                request.GET.get('latest_action_text')
            )

        for action in last_parsing_action:
            latest_parsing_actions[action.id] = action.text

        return JsonResponse(latest_parsing_actions)


    def get_necessary_information(self, request: WSGIRequest) -> JsonResponse:
        """ Получаем необходимую информацию """

        if 'parsing_action' in request.GET:
            return self.get_latest_parsing_actions(request)
        else:
            message = f'Такой аргумент не обрабатывается {request.GET}'
            raise ValueError(message)
